package timer_helper

import (
	"log"
	"sync"
	"sync/atomic"
	"time"
)

// 以下配置意义为: 本实例中的环形队列每小时转一圈, 每圈 3600 * 10 个点
const circleSize = 3600 * 10

// 时间精度: 100毫秒, 即0.1秒
const precision = time.Millisecond * 100

type None struct{}

type timerHelper struct {
	cur           int
	items         map[int]*timerItem
	notifyChan    chan None
	circleRunning atomic.Bool
	lock          sync.RWMutex
	itemId        atomic.Int64
	ticker        *time.Ticker
}

type timerItem struct {
	id                int
	ticker            bool
	callFunc          timerCallFunc
	callFuncExecuting atomic.Bool
	loop              int
	index             int
	duration          int
	pre               *timerItem
	next              *timerItem
}

var th *timerHelper
var lock sync.Mutex

func Instance() ITimerHelper {
	lock.Lock()
	defer lock.Unlock()
	if th == nil {
		th = &timerHelper{}
		th.notifyChan = make(chan None, 10)
		th.items = make(map[int]*timerItem)
		th.itemId.Store(1000)
		th.ticker = time.NewTicker(precision)
		th.ticker.Stop()
		go th.start()
	}
	return th
}

func (th *timerHelper) start() {
	for {
		select {
		case <-th.notifyChan:
			if !th.circleRunning.Load() {
				th.circleRunning.Store(true)
				go th.run()
			}
		}
	}
}

func (th *timerHelper) run() {
	defer func() {
		th.ticker.Stop()
		th.circleRunning.Store(false)
	}()
	th.ticker.Reset(precision)
	for {
		select {
		case <-th.ticker.C:
			th.addCur()
			if head, ok := th.items[th.cur]; ok {
				th.dealItem(th.cur, head)
			}
			if !th.hasItems() {
				return
			}
		}
	}
}

func (th *timerHelper) addCur() {
	th.cur += 1
	if th.cur == circleSize {
		th.cur = 0
	}
}

func (th *timerHelper) doCallFunc(item *timerItem) {
	if th.hit(item) {
		go func(item *timerItem) {
			if !item.callFuncExecuting.Load() {
				item.callFuncExecuting.Store(true)
				item.callFunc()
				item.callFuncExecuting.Store(false)
			}
		}(item)
	}
	if item.next != nil {
		th.doCallFunc(item.next)
	}
}

func (th *timerHelper) hit(item *timerItem) bool {
	return item.loop == 0 && item.index == th.cur
}

func (th *timerHelper) updateItemLoop(item *timerItem) {
	if item == nil {
		return
	}
	if item.index == th.cur {
		if item.loop == 0 && item.ticker {
			item.loop, item.index = th.getLoopAndIndex(item.duration)
		} else {
			item.loop -= 1
		}
	}
	if item.next != nil {
		th.updateItemLoop(item.next)
	}
}

// 执行超时任务或周期任务的callFunc, 如果是超时任务, 则移除超时任务
func (th *timerHelper) dealItem(key int, head *timerItem) {
	var item = head.next
	th.doCallFunc(item)
	for item != nil {
		if item.ticker {
			item = item.next
			continue
		}
		// 移除超时任务
		if th.hit(item) {
			th.removeItem(key, item)
		}
		item = item.next
	}
	th.updateItemLoop(head.next)
	// 调整items
	th.updateItems(key, head)
}

// updateItems 调整items中item.index != key的item
func (th *timerHelper) updateItems(key int, head *timerItem) {
	var item = head.next
	var next *timerItem
	for item != nil {
		next = item.next
		if item.index != key {
			th.removeItem(key, item)
			th.addItem(item)
		}
		item = next
	}
}

func (th *timerHelper) removeItem(key int, item *timerItem) {
	th.lock.Lock()
	defer th.lock.Unlock()
	if item.pre != nil {
		item.pre.next = item.next
		if item.next != nil {
			item.next.pre = item.pre
		}
	}
	if head, ok := th.items[key]; ok && head.next == nil {
		delete(th.items, key)
	}
}

func (th *timerHelper) hasItems() bool {
	th.lock.RLock()
	defer th.lock.RUnlock()
	return len(th.items) > 0
}

func (th *timerHelper) notify() {
	th.notifyChan <- None{}
}

func (th *timerHelper) getLoopAndIndex(duration int) (int, int) {
	var loop = duration / circleSize
	var index = (duration + th.cur) % circleSize
	if th.cur == index {
		loop -= 1
	}
	return loop, index
}

func (th *timerHelper) addItem(item *timerItem) {
	th.lock.Lock()
	defer th.lock.Unlock()
	item.next = nil
	item.pre = nil
	var head *timerItem
	var ok bool
	if head, ok = th.items[item.index]; ok {
		for head.next != nil {
			head = head.next
		}
	} else {
		head = &timerItem{}
		th.items[item.index] = head
	}
	head.next = item
	item.pre = head
}

func (th *timerHelper) add(sec int, ticker bool, callFunc timerCallFunc) int {
	if sec <= 0 {
		log.Panic("时间间隔应大于0")
	}
	var duration = sec * int(time.Second/precision)
	var loop, index = th.getLoopAndIndex(duration)
	th.itemId.Add(1)
	item := &timerItem{
		id:       int(th.itemId.Load()),
		ticker:   ticker,
		loop:     loop,
		index:    index,
		duration: duration,
		callFunc: callFunc,
	}
	th.addItem(item)
	th.notify()
	return item.id
}

func (th *timerHelper) AddTimeout(seconds int, callFunc timerCallFunc) int {
	return th.add(seconds, false, callFunc)
}
func (th *timerHelper) AddTicker(seconds int, callFunc timerCallFunc) int {
	return th.add(seconds, true, callFunc)
}
func (th *timerHelper) Remove(id int) bool {
	for k, v := range th.items {
		var item = v.next
		for item != nil {
			if item.id == id {
				th.removeItem(k, item)
				return true
			}
		}
	}
	return false
}
